use std::fs;
use std::path::PathBuf;
use std::sync::Arc;

use sea_orm::{ActiveModelTrait, IntoActiveModel, Set};

use crate::database::db_service::DbService;
use crate::handler::utils;
use crate::{config::Config, errors::FreightResult};

#[derive(Clone)]
pub struct BlockOptions {
    pub block_service: Arc<DbService>,
    pub crates_path: PathBuf,
    pub block_crates: PathBuf,
    pub crate_name: String,
    pub crate_version: String,
}

impl BlockOptions {
    pub fn new(config: Config, block_service: Arc<DbService>, name_version: String) -> Self {
        let name_version: Vec<&str> = name_version.split(':').collect();
        assert!(
            name_version.len() == 2,
            "Please provide a valid crates name, for example: tokio-rs:1.0.0"
        );

        Self {
            block_service,
            crates_path: config.crates_path,
            block_crates: config.block_crates,
            crate_name: name_version[0].to_string(),
            crate_version: name_version[1].to_string(),
        }
    }
}

pub async fn block(opts: BlockOptions) -> FreightResult {
    let file = utils::get_file_path(&opts.crates_path, &opts.crate_name, &opts.crate_version);
    let new_path: PathBuf =
        utils::get_file_path(&opts.block_crates, &opts.crate_name, &opts.crate_version);
    if file.exists() {
        fs::create_dir_all(new_path.parent().unwrap()).unwrap();
        fs::rename(file, new_path)?;
    }
    opts.block_service
        .save_block_crates(&opts.crate_name, &opts.crate_version)
        .await
        .unwrap();
    Ok(())
}

pub async fn unblock(opts: BlockOptions) -> FreightResult {
    let to_path = utils::get_file_path(&opts.crates_path, &opts.crate_name, &opts.crate_version);
    let from_path: PathBuf =
        utils::get_file_path(&opts.block_crates, &opts.crate_name, &opts.crate_version);

    if from_path.exists() {
        fs::rename(from_path, to_path)?;
    }

    let record = opts
        .block_service
        .get_block_by_name_version(&opts.crate_name, &opts.crate_version)
        .await
        .unwrap();
    if let Some(record) = record {
        let mut record = record.into_active_model();
        record.blocked = Set(false);
        record
            .save(opts.block_service.get_connection())
            .await
            .unwrap();
    }
    Ok(())
}
